/**
 * Session 和 Cookie 是 Web 开发中用于跟踪用户状态、维持会话的核心机制，主要用于解决 HTTP 协议无状态的特性。两者的核心区别与联系如下：
一、Cookie‌
    ‌存储位置‌：存储在‌客户端浏览器‌中（文本文件形式）。
    ‌工作原理‌：服务器通过响应头 Set-Cookie 将数据发送给浏览器，浏览器后续请求自动携带 Cookie 信息发送至服务器。
    ‌特点‌：
    ‌存储容量小‌：单个 Cookie 通常 ≤ 4KB，单个域名下的 Cookie 数量有限制（约 50 个）。
    ‌生命周期可控‌：可设置过期时间（如 Expires 或 Max-Age），支持长期存储（如“记住我”功能）。
    ‌安全性较低‌：数据存储在客户端，易被 XSS/CSRF 攻击窃取或篡改。
    ‌适用场景‌：存储非敏感信息（如用户偏好、语言设置）或 Session ID。
二、Session‌
    ‌存储位置‌：数据存储在‌服务器端‌（内存、数据库或文件系统中），客户端仅保存一个唯一标识 ‌Session ID‌。
    ‌工作原理‌：
    用户首次访问时，服务器创建 Session 并生成 Session ID，通过 Set-Cookie 发送给浏览器。
    浏览器后续请求携带 Session ID，服务器据此检索对应的会话数据（如登录状态、购物车内容）。
    ‌特点‌：
    ‌存储容量大‌：数据存储在服务器，容量由服务器配置决定（如 PHP 默认 128MB）。
    ‌生命周期短‌：默认随浏览器关闭而失效（依赖 Session ID 的 Cookie 过期），服务器可设置超时时间。
    ‌安全性较高‌：敏感数据（如用户凭证）存储在服务端，仅通过 Session ID 关联客户端。
    ‌性能影响‌：服务器需存储和检索会话数据，高并发时可能成为瓶颈
三、区别对比总结‌
    ‌特性‌	 ‌‌Cookie‌	 ‌‌ ‌ ‌ ‌ ‌ ‌ ‌             ‌Session‌
‌    存储位置‌	客户端	                     服务器端
‌    数据安全性‌	低（易被窃取）	             高（仅传递 ID）
    ‌存储容量‌	≤4KB/个，数量受限	           由服务器配置决定
    ‌生命周期‌	可长期保存（设置过期时间）	 会话级（默认随浏览器关闭失效）
    ‌适用场景‌	非敏感信息（偏好设置、跟踪）  敏感数据（登录状态、购物车）
四、核心联系‌
    ‌协同工作‌：Session ‌依赖 Cookie 传递 Session ID‌（如 PHPSESSID）。若浏览器禁用 Cookie，可通过 URL 重写（如 ?sessionid=xxx）实现 Session 跟踪。
    ‌互补角色‌：
    Cookie 存储小量、非关键数据（如 Session ID）实现状态保持。
    Session 存储大量、敏感数据，确保安全性。
    例如用户登录场景：服务器验证账号后，生成 Session 存储用户 ID，并通过 Cookie 将 Session ID 发送至浏览器；后续请求中，浏览器自动携带该 Cookie，服务器据此识别用户身份
 */
// 防抖
function debounce(func, delay = 1000) {
    let timer = null
    return function(...args) {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            func.apply(this, args)
        }, delay)
    }
}
// 节流
function throttle(func, limit = 1000) {
    let lastTime = 0
    return function(...args) {
        const now = new Date().getTime()
        if (now - lastTime >= limit) {
            func.apply(this, args)
            lastTime = now
        }
    }
}
// 柯里化函数
function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args)
        } else {
            // 返回一个新函数接受参数
            return function(...restArgs) {
                return curried.apply(this, args.concat(restArgs))
            }
        }
    }
}
// 组合函数
function compose(...funcs) {
    /**如果没有函数 就返回一个恒等函数，输入什么返回什么 */
    if (funcs.length === 0) {
        return arg => arg
    }
    /**如果只有一个函数 直接返回它 */
    if (funcs.length === 1) {
        return funcs[0]
    }
    /**
     * 使用 reduce 构建组合函数
     * a 是积累的组合函数 b是当前需要加入的函数
     * (a,b) => (...args) => a(b(...args))
     * args -> b -> a(b(...args))
     * 从右向左链式调用
     */
    return funcs.reduce((a,b) => (...args) => a(b(...args)))
}
const add1 = x => x + 1
const multiply2 = x => x * 2
const subtract3 = x => x - 3
const processedValue = compose(subtract3, multiply2, add1)
/**
 * compose 从右向左链式调用
 * 1：执行add1(5) -> 6
 * 2: 执行multiply2(6) -> 12
 * 3: 执行subtract3(12) -> 9
 * 最终结果是9
 */
console.log(processedValue(5))
// 函数记忆化
function memoize(fn) {
    // 1.初始化缓存
    const cache = {}
    // 2.返回一个新函数，这个新函数是带有记忆化能力的函数
    return function(...args) {
        // 3.生成缓存key 
        // 简单版本：单个参数直接使用，多个参数转JSON(有局限性)
        const key = args.length === 1 ? args[0] : JSON.stringify(args)
        // 4.检查缓存
        if (cache.hasOwnProperty(key)) {
            // 5.直接返回缓存结果
            return cache[key]
        } else {
            // 6.调用原函数计算结果
            const result = fn.apply(this, args)
            // 7.将结果存入缓存
            cache[key] = result
            // 8.返回计算结果
            return result
        }
    }
}
// 提取url 参数
function queryUrlParams(urlStr) {
    const urlParams = urlStr.split('?')[1]
    const urrSearchParams = new URLSearchParams(urlParams)
    const params = Object.fromEntries(urrSearchParams.entries())
    return params
}
// 数组随机排序
function sortArray(arr) {
    for(let i=0;i < arr.length;i++){
        let randomIndex = parseInt(Math.random() * arr.length)

        let temp = arr[i]
        arr[i] = arr[randomIndex]
        arr[randomIndex] = temp
    }
    return arr
}
// 100 内的质数
function printZn(num) {
    let count = 0
    for(let i = 2;i<num;i++){
        for(let j=1;j<i;j++) {
            if (i % j === 0) {
                count ++
            }
        }
        if (count === 2) {
            console.log(i)
        }
        count = 0
    }
}
// flatten 数组
function flatArr(arr) {
    while(arr.some(v => Array.isArray(v))) {
        arr = [].concat(...arr)
    }
    return arr
    // return [].concat(...arr.map(v => (Array.isArray(v) ? flatArr(v) : v)))
}
// 两数之和返回对应的下标
function twoSum(nums, target) {
    for(let i=0;i < nums.length;i++) {
        const num = nums[i]
        const targetIndex = nums.indexOf(target - num)

        if (targetIdex > -1 && targetIdex !== i) {
            return [i, targetIndex]
        }
    }
}
// 发布订阅
class EventEmitter {
    handlers = {}
    on (type, handler, once = false){
        if (!this.handlers[type]) {
            this.handlers[type] = []
        }
        if (!this.handlers[type].includes(handler)) {
            this.handlers[type].push(handler)
            handler.once = once
        }
    }
    once(type, handler) {
        this.on(type, handler, true)
    }
    off(type, handler) {
        if (this.handlers[type]) {
            this.handlers[type] = this.handlers[type].filter(h => {
                return h !== handler
            })
        }
    }
    trigger(type) {
        if(this.handlers[type]) {
            this.handlers[type].forEach(handler => {
                handler.call(this)
                
                if (handler.once) {
                    this.off(type, handler)
                }
            })
        }
    }
}
